package jmine.tec.sync;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;

import jmine.tec.rpc.common.entity.EntityRepresentation;
import jmine.tec.utils.register.Receiver;
import bancosys.tec.persist.Persistable;
import bancosys.tec.persist.dao.BeanNotFoundException;

/**
 * {@link AbstractEntityMaterializer} que materializa varios tipos de entidades, todas elas subclasses da classe passada. A representacao
 * deve ser tambem uma superclasse em comum
 * 
 * @author takeshi
 * @param <A> A
 */
public class MultipleRepresentationEntityMaterializer<A extends Persistable> extends AbstractEntityMaterializer<A> implements
        Receiver<EntityMaterializer> {

    private final List<EntityMaterializer> delegates = new CopyOnWriteArrayList<EntityMaterializer>();

    private final Class<? extends EntityRepresentation> baseType;

    private final Class<A> localEntityClass;

    /**
     * C'tor
     * 
     * @param baseType a classe base - superclasse de todas
     * @param localEntityClass a classe
     */
    public MultipleRepresentationEntityMaterializer(Class<? extends EntityRepresentation> baseType, Class<A> localEntityClass) {
        super();
        this.baseType = baseType;
        this.localEntityClass = localEntityClass;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected Class<A> localEntityClass() {
        return this.localEntityClass;
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    @Override
    public A updateEntity(Serializable remotePrimaryKey, Serializable naturalKey) {
        for (EntityMaterializer<?> delegate : this.delegates) {
            List<? extends Class<?>> list = delegate.remoteRepresentations();
            try {
                Map<Class<?>, Object> map = retrieveRepresentationsFrom(remotePrimaryKey, list, this.getRestServiceProvider());
                return (A) delegate.updateEntity(remotePrimaryKey, map);
            } catch (BeanNotFoundException e) {
                // ignore
            }
        }
        this.entityDeleted(remotePrimaryKey, naturalKey);
        return null;
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    @Override
    public A updateEntity(Serializable remotePrimaryKey, Map<Class<?>, Object> representations) {
        for (EntityMaterializer<?> delegate : this.delegates) {
            List<? extends Class<?>> list = delegate.remoteRepresentations();
            try {
                Map<Class<?>, Object> map = retrieveRepresentationsFrom(remotePrimaryKey, list, this.getRestServiceProvider());
                return (A) delegate.updateEntity(remotePrimaryKey, map);
            } catch (BeanNotFoundException e) {
                // ignore
            }
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void entityDeleted(Serializable remotePrimaryKey, Serializable naturalKey) {
        for (EntityMaterializer<?> delegate : this.delegates) {
            delegate.entityDeleted(remotePrimaryKey, naturalKey);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void entityDeleted(Serializable remotePrimaryKey) {
        for (EntityMaterializer<?> delegate : this.delegates) {
            delegate.entityDeleted(remotePrimaryKey);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void doUpdateEntity(A entity, Map<Class<?>, Object> representations, Serializable requestedId) {
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public List<? extends Class<? extends EntityRepresentation>> remoteRepresentations() {
        return Arrays.asList(this.baseType);
    }

    /**
     * {@inheritDoc}
     */
    public void register(Collection<? extends EntityMaterializer> registers) {
        this.delegates.addAll(registers);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Serializable naturalKeyFromRepresentations(Serializable remoteId, Map<Class<?>, Object> representations) {
        for (EntityMaterializer<?> delegate : this.delegates) {
            List<? extends Class<?>> list = delegate.remoteRepresentations();
            try {
                Map<Class<?>, Object> map = retrieveRepresentationsFrom(remoteId, list, this.getRestServiceProvider());
                return delegate.naturalKeyFromRepresentations(remoteId, map);
            } catch (BeanNotFoundException e) {
                // ignore
            }
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public boolean isMappedByNaturalKey() {
        for (EntityMaterializer<?> delegate : this.delegates) {
            if (delegate.isMappedByNaturalKey()) {
                return true;
            }
        }
        return false;
    }

}
